#include "scripthelper.h"

/*
#include "luaplus/include/lua.h"
#include "luaplus/include/lauxlib.h"
#include "luaplus/include/lualib.h"
*/

#include "util/mmgr.h"
#include "util/log.h"
#include "vfs/vfs.h"


#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif //_ENABLE_LUABIND

using namespace LuaPlus;
using namespace script; 

int	ScriptHelper::GetInt ( LuaPlus::LuaObject&  _Obj, int _Default )
{
	if (_Obj.GetState()==NULL) return _Default;	// Object is not initialized
	if (_Obj.IsNil()) return _Default;	// failed to find obj
	if (!_Obj.IsNumber()) return _Default;	// not a number
	return _Obj.GetInteger();
}

int	ScriptHelper::GetInt ( LuaState & _State, 
                         const std::string & _Table, 
                         const std::string & _Name, 
                         int _Default )
{
	if (_State.GetCState()==NULL) return _Default;	// Object is not initialized
	LuaObject Table = GetObj(_State, _Table);	// Find the table it's in.
	LuaObject Obj   = GetObj(Table, _Name);	// Find object
	if (Obj.GetState()==NULL) return _Default;	// Object is not initialized

	return GetInt(Obj, _Default);
}

int ScriptHelper::GetTableInt ( LuaPlus::LuaObject& _Object, 
                              const std::string & _Name, 
                              int _Default )
{
	LuaObject Obj   = GetObj(_Object, _Name);	// Find object
	return GetInt(Obj, _Default);
}


bool ScriptHelper::GetBool( LuaPlus::LuaObject&  _Obj, bool _Default )
{
	if (_Obj.GetState()==NULL) return _Default;	// Object is not initialized
	if (_Obj.IsNil()) return _Default;	// failed to find obj
	if (_Obj.IsBoolean()) return _Obj.GetBoolean();
	if (!_Obj.IsNumber()) return _Default;	// not a number
	return (_Obj.GetInteger() == 1);
}

bool ScriptHelper::GetBool( LuaState & _State, 
                         const std::string & _Table, 
                         const std::string & _Name, 
                         bool _Default )
{
	if (_State.GetCState()==NULL) return _Default;	// Object is not initialized
	LuaObject Table = GetObj(_State, _Table);	// Find the table it's in.
	LuaObject Obj   = GetObj(Table, _Name);	// Find object
	if (Obj.GetState()==NULL) return _Default;	// Object is not initialized

	return GetBool(Obj, _Default);
}

bool ScriptHelper::GetTableBool( LuaPlus::LuaObject& _Object, 
                              const std::string & _Name, 
                              bool _Default )
{
	LuaObject Obj   = GetObj(_Object, _Name);	// Find object
	if (Obj.IsBoolean())
		return GetBool(Obj, _Default);
	else 
	return GetInt(Obj, _Default)!=0;
}


float ScriptHelper::GetFloat ( LuaPlus::LuaObject&  _Obj, float _Default )
{
	if (_Obj.GetState()==NULL) return _Default;	// Object is not initialized
	if (_Obj.IsNil()) return _Default;	// failed to find obj
	if (!_Obj.IsNumber()) return _Default;	// not a number
	return (float)_Obj.GetNumber();
}

float ScriptHelper::GetFloat ( LuaState & _State, 
                             const std::string & _Table, 
                             const std::string & _Name, 
                             float _Default )
{
	if (_State.GetCState()==NULL) return _Default;	// Object is not initialized
	LuaObject Table = GetObj(_State, _Table);	// Find the table it's in.
	LuaObject Obj   = GetObj(Table, _Name);	// Find object
	if (Obj.GetState()==NULL) return _Default;	// Object is not initialized

	return GetFloat(Obj, _Default);
}

float ScriptHelper::GetTableFloat ( LuaPlus::LuaObject& _Object, 
                                  const std::string & _Name, 
                                  float _Default )
{
	LuaObject Obj   = GetObj(_Object, _Name);	// Find object
	return GetFloat(Obj, _Default);
}

std::string ScriptHelper::GetString ( LuaPlus::LuaObject&  _Obj, 
                                    const std::string & _Default )
{
	if (_Obj.GetState()==NULL) return _Default;	// Object is not initialized
	if (_Obj.IsNil()) return _Default;	// failed to find obj
	if (!_Obj.IsString()) return _Default;	// not a string
	return _Obj.GetString();
}

std::string	ScriptHelper::GetString (	LuaState & _State, 
                                    const std::string & _Table, 
									const std::string & _Name, 
									const std::string & _Default)
{
	if (_State.GetCState()==NULL) return _Default;	// Object is not initialized
	LuaObject Table = GetObj(_State, _Table);	// Find the table it's in.
	LuaObject Obj   = GetObj(Table, _Name);	// Find object
	if (Obj.GetState()==NULL) return _Default;	// Object is not initialized

	return GetString(Obj, _Default);
}

std::string ScriptHelper::GetTableString ( LuaPlus::LuaObject& _Object, 
                                         const std::string & _Name, 	
                                         const std::string & _Default )
{
	if (!_Object.IsTable()) return _Default;
	LuaObject Obj   = GetObj(_Object, _Name);	// Find object
	if (Obj.IsNil()) return _Default;
	return GetString(Obj, _Default);
}


math::Vec4 ScriptHelper::GetVec4(LuaPlus::LuaObject& _Obj, const math::Vec4& _Default)
{
	if (_Obj.GetState()==NULL) return _Default;	// Object is not initialized
	if (_Obj.IsNil()) return _Default;	// failed to find obj
	if (!_Obj.IsTable()) return _Default;	// failed to find obj
	if (_Obj.GetTableCount()!=4)	return _Default;	// vector should have four components

	LuaObject TempObj1 = _Obj[1];
	LuaObject TempObj2 = _Obj[2];
	LuaObject TempObj3 = _Obj[3];
	LuaObject TempObj4 = _Obj[4];
	
	math::Vec4	Vector = math::Vec4(GetFloat(	TempObj1, _Default.x),
									GetFloat(	TempObj2, _Default.y),
									GetFloat(	TempObj3, _Default.z),
									GetFloat(	TempObj4, _Default.w)
									);
	return Vector;
}


math::Vec4 ScriptHelper::GetVec4(	LuaPlus::LuaObject& _State, 
									const std::string& _Table, 
									const std::string& _Name, 
									const math::Vec4& _Default)
{
	if (_State.GetCState()==NULL) return _Default;	// Object is not initialized
	LuaObject Table = GetObj(_State, _Table);	// Find the table it's in.
	LuaObject Obj   = GetObj(Table, _Name);	// Find object
	if (Obj.GetState()==NULL) return _Default;	// Object is not initialized

	return GetVec4(Obj, _Default);
}

math::Vec4 ScriptHelper::GetTableVec4(	LuaPlus::LuaObject& _Object, 
											const std::string& _Name, 
											const math::Vec4& _Default)
{
	LuaObject Obj   = GetObj(_Object, _Name);	// Find object
	return GetVec4(Obj, _Default);
}


math::Vec3 ScriptHelper::GetVec3(LuaPlus::LuaObject& _Obj, const math::Vec3& _Default)
{
	if (_Obj.GetState()==NULL) return _Default;	// Object is not initialized
	if (_Obj.IsNil()) return _Default;	// failed to find obj
	if (!_Obj.IsTable()) return _Default;	// failed to find obj
	if (_Obj.GetTableCount()!=3)	return _Default;	// vector should have three components
	
	LuaObject TempObj1 = _Obj[1];
	LuaObject TempObj2 = _Obj[2];
	LuaObject TempObj3 = _Obj[3];
	
	math::Vec3	Vector = math::Vec3(GetFloat(	TempObj1, _Default.x),
									GetFloat(	TempObj2, _Default.y),
									GetFloat(	TempObj3, _Default.z)
									);
	return Vector;
}


math::Vec3 ScriptHelper::GetVec3(	LuaPlus::LuaObject& _State, 
									const std::string& _Table, 
									const std::string& _Name, 
									const math::Vec3& _Default)
{
	if (_State.GetCState()==NULL) return _Default;	// Object is not initialized
	LuaObject Table = GetObj(_State, _Table);	// Find the table it's in.
	LuaObject Obj   = GetObj(Table, _Name);	// Find object
	if (Obj.GetState()==NULL) return _Default;	// Object is not initialized

	return GetVec3(Obj, _Default);
}

math::Vec3 ScriptHelper::GetTableVec3(	LuaPlus::LuaObject& _Object, 
											const std::string& _Name, 
											const math::Vec3& _Default)
{
	LuaObject Obj   = GetObj(_Object, _Name);	// Find object
	return GetVec3(Obj, _Default);
}


math::Vec2 ScriptHelper::GetVec2(LuaPlus::LuaObject& _Obj, const math::Vec2& _Default)
{
	if (_Obj.GetState()==NULL) return _Default;	// Object is not initialized
	if (_Obj.IsNil()) return _Default;	// failed to find obj
	if (!_Obj.IsTable()) return _Default;	// failed to find obj
	if (_Obj.GetTableCount()!=2)	return _Default;	// vector should have three components
	
	LuaObject TempObj1 = _Obj[1];
	LuaObject TempObj2 = _Obj[2];
	
	
	math::Vec2	Vector = math::Vec2(GetFloat(	TempObj1, _Default.x),
									GetFloat(	TempObj2, _Default.y));
	return Vector;
}


math::Vec2 ScriptHelper::GetVec2(	LuaPlus::LuaObject& _State, 
									const std::string& _Table, 
									const std::string& _Name, 
									const math::Vec2& _Default)
{
	if (_State.GetCState()==NULL) return _Default;	// Object is not initialized
	LuaObject Table = GetObj(_State, _Table);	// Find the table it's in.
	LuaObject Obj   = GetObj(Table, _Name);	// Find object
	if (Obj.GetState()==NULL) return _Default;	// Object is not initialized

	return GetVec2(Obj, _Default);
}

math::Vec2 ScriptHelper::GetTableVec2(	LuaPlus::LuaObject& _Object, 
                                        const std::string& _Name, 
										const math::Vec2& _Default)
{
	LuaObject Obj   = GetObj(_Object, _Name);	// Find object
	return GetVec2(Obj, _Default);
}

LuaObject ScriptHelper::GetObj ( LuaState & _State, const std::string & _Name )
{
	LuaObject Obj = _State.GetGlobal(_Name.c_str());
	return Obj;
}

LuaObject ScriptHelper::GetObj ( LuaPlus::LuaObject& _Object, const std::string & _Name )
{
	if (_Object.GetState()==NULL) return LuaObject();
	if (_Object.IsNil()) return LuaObject();

	return _Object[_Name.c_str()];
}


//////////////////////////////////////////////////////////////////
/// Register a library of LUA functions
/// these are all registered under the same table (class/namespace)
/// A library is defined like this:
/// static const luaL_reg MyLib[] = {
///   {"LuaName",				C_Name},
///   {"LuaName2",				C_Name2},
///   {NULL,                    NULL} };
///
/// Where C_Name and C_Name2 are functions with the following setup:
/// static int C_Name(lua_State *L)
/// 
/// Register a library like this:
/// 
/// ScriptHelper::RegisterLibrary(ScriptState, "MyLib", MyLib");
/// Inside ScriptState, you would now access your library function like this:
/// MyLib.LuaName();
bool ScriptHelper::RegisterLibrary ( LuaPlus::LuaState &  _State,
                                   const char * _Name, 
                                   const luaL_reg _lib[] )
{
/*    // Create it as usual
    lua_State * L = _State.GetCState();

        
    // Check to see if table exists already:
    LuaObject Component = ScriptHelper::GetObj(_State, _szName);
    if (Component.IsNil())  // Need to create the table ?
    {
	    lua_pushlstring(L, _szName, strlen(_szName));
        lua_newtable(L);
    }
	luaL_openlib(L, _szName, _lib, 0);
    lua_rawset(L, LUA_GLOBALSINDEX);  // Close*/

	LuaObject Obj = _State.GetGlobal(_Name);
    if (Obj.IsNil())  // Need to create the table ?
    {
        lua_State * L = _State.GetCState();
	    lua_pushlstring(L, _Name, strlen(_Name));
        lua_newtable(L);
        lua_rawset(L, LUA_GLOBALSINDEX);  // Close
        
        Obj = _State.GetGlobal(_Name);   // Verify ?
        if (Obj.IsNil())  // Need to create the table ?
        {
            return false;
        }
    } 
    // Open lib
    int i=0;
    while (_lib[i].name != NULL)
    {
        Obj.Register(_lib[i].name, _lib[i].func);
        i++;
    }

    return 1;
}

//////////////////////////////////////////////////////////////////
/// Load a Lua file, with setup and error handling
/// 
bool ScriptHelper::DoFile ( LuaState & _State, const std::string & _Name )
{
    if (_Name.empty())
    {
        _LOG(MSG_ERROR, "Failed to load script file " << _Name << ". Program may become unstable.");	// accessing log file
        return false;
    }
#ifdef _LOAD_PRECOMPILED_LUA
	std::string	LuacName = vfs::VFS::StripExtension(_Name) + ".luac";
	if (vfs::VFS::Instance().FileExists(LuacName))
	{
		vfs::LoadedBlock Block;
		if (vfs::VFS::Instance().GetFile(LuacName, Block))
		{
			return DoBlock(_State, Block);
		} 
	}
#endif  // _LOAD_PRECOMPILED_LUA
	vfs::LoadedBlock Block;
	if (!vfs::VFS::Instance().GetFile(_Name, Block))
	{
		_LOG(MSG_SCRIPT, "Unable to load script file " << _Name);
		return false;
	} 
	return DoBlock(_State, Block);
}

bool ScriptHelper::DoBlock ( LuaPlus::LuaState & _State, 	vfs::LoadedBlock& Block )
{
	int Status = _State.DoBuffer((char*)Block.DataArray, Block.Size, NULL);

	if (Status!=0)
	{
		_LOG(MSG_SCRIPT, "Unable to parse script file " << Block.FileName);
		return false;
	}

	// Successfull..    
	_LOG(MSG_INIT, "LUA Script loaded from " << Block.FileName);	// accessing log file
    return true;
}
 
bool ScriptHelper::CreateTable ( LuaState & _State, const std::string & _Name )
{
    LuaObject Obj = _State.GetGlobal(_Name.c_str());
    if (Obj.IsNil())  // Need to create the table ?
    {
        lua_State * L = _State.GetCState();
	    lua_pushlstring(L, _Name.c_str(), strlen(_Name.c_str()));
        lua_newtable(L);
        lua_rawset(L, LUA_GLOBALSINDEX);  // Close
    }
    return false;   // Already exists..
}

static int	ScriptHelperLog( LuaState* state )
{
	LuaStack args(state);

	std::string	Output;
	for (int i=1; i<=args.Count(); i++)
	{
		LuaObject	Obj(args[i]);
		const char*		pString = Obj.ToString();
		if (pString)
		{
			Output+=pString;
		} else
		{
			int j=0;
		}
	}
	if (!Output.empty())
	{
		_LOG(MSG_SCRIPT, Output);
	}
	return 0;
}
static int ScriptHelperLoad( LuaState* state )
{
	LuaStack args(state);
	if (args.Count() == 0)
	{
		_LOG(MSG_SCRIPT, "Unable to load file!");
		return 0;
	}
	LuaObject Filename(args[1]);

	if (!Filename.IsString())
	{
		_LOG(MSG_SCRIPT, "Unable to load file!");
		return 0;
	} else
	{
		vfs::LoadedBlock Block;
		if (!vfs::VFS::Instance().GetFile(Filename.ToString(), Block))
		{
			_LOG(MSG_SCRIPT, "Unable to load script file " << Filename.ToString());
		} else
		{
			if (state->DoBuffer((char*)Block.DataArray, Block.Size, NULL) != 0)
			{
				_LOG(MSG_SCRIPT, "Error executing block from " << Filename.ToString());
			}
		}
	}
	return 0;
}


#ifdef _PLATFORM_WII
static void*	ScriptHelperCustomFreallocator(void *ud, void *ptr, size_t osize, size_t nsize, const char* allocName, unsigned int flags)
{
  if (nsize == 0) 
  {
    MMGR_Free(ptr);
    return NULL;
  }
  else
    return MMGR_Realloc(ptr, osize, nsize, 4);
}
#else // _PLATFORM_WII
static void*	ScriptHelperCustomFreallocator(void *ud, void *ptr, size_t osize, size_t nsize, const char* allocName, unsigned int flags)
{
  if (nsize == 0) 
  {
    free (ptr);
    return NULL;
  }
  else
  {
	  void*	pNew = realloc(ptr, nsize);
	  if (!ptr)
	  {
		memset(pNew, 0, nsize);
	  }
	  return pNew;
  }

}
#endif // _PLATFORM_WII

LuaState * ScriptHelper::CreateLuaState ( )
{
    lua_setdefaultallocfunction(ScriptHelperCustomFreallocator, NULL);

    LuaState * state = LuaPlus::LuaState::Create(true);
	assert(state && "Unable to create state!");

	lua_State* L	= state->GetCState ( );
#ifdef _ENABLE_LUABIND
	luabind::open(L); // initialize LuaBind
#endif //_ENABLE_LUABIND

    if ( state )
    {
		state->GetGlobals ( ).Register ( "LOG", ScriptHelperLog );
	    state->GetGlobals ( ).Register ( "_ALERT", ScriptHelperLog );
	    state->GetGlobals ( ).Register ( "print", ScriptHelperLog );
	    state->GetGlobals ( ).Register ( "dofile", ScriptHelperLoad);
    }

    return state;
}


